package inutan.game.action;

import bright.serialization.ByteBuf;
import inutan.common.inner.protocol.InnerMessage;
import inutan.common.kit.InnerKit;
import inutan.game.business.GameFlowContext;
import inutan.game.event.model.LoginDelayEvent;
import inutan.game.event.model.LoginEvent;
import inutan.game.event.model.RoleLevelUpEvent;
import inutan.game.service.UserService;
import luban.common.Command;
import luban.common.LongValue;
import luban.user.LoginRequest;
import org.ricks.common.event.EventBus;
import org.ricks.common.lang.Logger;
import org.ricks.ioc.anno.Action;
import org.ricks.ioc.anno.ActionMethod;
import org.ricks.ioc.anno.Autowired;
import org.ricks.net.DataCodecKit;
import org.ricks.net.DefaultPacket;
import org.ricks.orm.OrmContext;
import java.util.Date;


/**
 * @author ：demon-chen
 * 来自业务集群的消息 使用自定义消息体
 * 如果1:1 对应 ，
 */
@Action(modularId = 1)
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * return 是直接写回 context userId 玩家
     * 如果需要广播给所有玩家，拿到所有的broker client 即可
     * 登录成功 broker server 缓存 玩家基础信息 & 设置登录成功状态
     * 聊天 匹配房间 交互小游戏 都需要带上玩家基础信息 比如： 头像 姓名 等级  sd时装....      根据实际游戏业务需求缓存 ？
     * broker 维护基础信息 ，还是 match server 维护，如果match server维护，就需要 business server 转发，或者 创建房间 进入房间 带上
     * broker 设置登录状态，可以在网关层面进行请求访问登录拦截
     * 登录分配businessId业务节点处理登录请求， 登录成功broker 还需要userId 和 businessId 进行绑定，退出游戏 进行解绑，这样玩家从登录到退出 生命周期都在一个businessId业务节点上面处理
     * 如果玩家业务节点崩溃 或者 删除业务节点，也是先保存节点上玩家的数据，然后从存活的业务节点分配节点重新 load数据
     *
     * 玩家登录
     * 重复登录(5分钟内session 相同，登录load的是玩家缓存。 5分钟之后会 close session & 清除cache & 写db ,重新load 是从db load 【不同的session 就意味不同的sessionId 分配相同的业务节点，不然需要其它节点write db 之后 才能load 】)
     * 玩家顶号 (不同IP登录？)
     *
     * 如果以 userId 作为主键 load 数据，外挂模拟 userId 请求登录 userId 不存在则会自动创建。或则游戏工坊 是否会存在很多垃圾账号
     * @param context
     * @return
     */
    @ActionMethod(cmd = Command.Login)
    public void login(GameFlowContext<DefaultPacket> context) {
        long s = System.nanoTime();
        Logger.info(Thread.currentThread().getName()+"玩家开始登录。。。。");
        /**             实时派发事件           */
        EventBus.me().publish(new LoginEvent(context.getUserId()));

        EventBus.me().publish(new RoleLevelUpEvent(context,1 ,1 ));

        long ep = System.nanoTime();
        Logger.info("实时派发事件耗时："+ (ep-s));
        /**             延迟派发事件           */
        LoginDelayEvent scheduledEvent = new LoginDelayEvent();
        scheduledEvent.setEndTime(new Date((System.currentTimeMillis() + 10000L)));
        EventBus.me().publish(scheduledEvent);
        long se = System.nanoTime();
        Logger.info("延迟派发事件耗时："+ (se-s));
        /**             登录逻辑              */
        DefaultPacket defaultPacket = context.getPacket();
        LoginRequest loginRequest = DataCodecKit.decode(defaultPacket.getPacket(),LoginRequest.class);
        Logger.info( loginRequest.toString());
        long userId = userService.loginValidate(loginRequest); //登录验证，不用做任何其他处理。断言异常机制 会直接把error code 推送给客户端。请求没有中断说明验证通过
        long lv = System.nanoTime();
        System.err.println("登录验证耗时："+ (lv-s));
        userService.login(context,userId); //处理实际的登录业务
        long e = System.nanoTime();
        Logger.info(" 登录耗时 t:" + (e-s));

    }



    @ActionMethod(cmd = 101)
    public void wish(GameFlowContext context) {
        Logger.info("开始处理登录业务。。。。");
    }

    /**
     * 缓存机制过期的key ，进行redis 解绑
     * 解绑后，登录服重新分配 & 绑定
     * 登录服 登录成功后 带入网关节点
     *
     * 用户退出
     * 清楚缓存，db异步回写容器销毁，由容器写超时触发。
     * 异步回写容器销毁，才会解除与玩家的绑定关系
     */
    @ActionMethod(cmd = 5000)
    public void userExit(GameFlowContext context) {
        /**
         * 避免阻塞业务线程
         */
        final long userId = context.getUserId();
        Thread.ofVirtual().name("user-exit",userId).start(() -> {
            Logger.info("用户退出游戏 userId:"+ userId);
            OrmContext.me().destroy(userId,() -> {
                System.err.println("玩家数据销毁成功！");
            });
            /**
             * 网关RPC同步调用，返回
             */
            ByteBuf buf = new ByteBuf();
            LongValue longValue = new LongValue();
            longValue.value = userId;
            longValue.serialize(buf);
            InnerMessage innerMessage = InnerKit.createInnerMessage((short) 5000, buf.array());
            context.sendResponse(innerMessage);
        });
    }

//    /**
//     * 网关启动 重启，通知业务节点。
//     * 业务节点告知网关，模块ID  ID  cmd  在线人数
//     * @param context
//     */
//    @ActionMethod(cmd = Command.LoginBroker)
//    public void brokerIntoGame(GameFlowContext context) {
////        LoginBroker loginBroker = new LoginBroker();
////        loginBroker.type = ConfigConstant.MODULE_ID;
////        loginBroker.serverId = ConfigConstant.ID;
////        HashSet<Short> allCmd = new HashSet<>();
////        allCmd.addAll(ActionMethodManager.allCmd());
////        loginBroker.bitCmd = allCmd;
////        InnerMessage innerMessage = InnerKit.createInnerMessage(Command.LoginBroker, loginBroker);
////        context.sendResponse(innerMessage);
////        BrokerManager.me().changeUserOnlineCount();            //解除 user  <->  businessId 绑定关系
//    }

//    /**
//    * @Explain --> GM增加网关节点 连接到新的网关节点
//    * @Author yanwang
//    * @Date 2023/5/10 10:07
//    **/
//    @ActionMethod(cmd = Command.AddBroker)
//    public void addBroker(GameFlowContext context){
//        GMBrokerInfo info = DataCodecKit.decode(context.packet(),GMBrokerInfo.class);
//        BrokerMessage message = new BrokerMessage();
//        message.innerPort = info.innerPort;
//        message.innerHost = info.innerHost;
//    }
//
//    /**
//    * @Explain --> 仅限内网环境 登录到另一个业务节点时 刷新保存上一个业务节点的内存数据
//     * 仅内网环境使用，正式环境保障了玩家登录相同游戏节点
//    * @Author yanwang
//    * @Date 2023/5/10 10:08
//    **/
//    @ActionMethod(cmd = Command.InnerFlushData)
//    public void innerFlushData(GameFlowContext context){
//        AsyncWriteManager.me().syncFlushAll();
//    }


}
